#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    using namespace LockModule;
    using namespace CondModule;

    static const int gcap = 10; //最大容量

    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull()  //是否满
        {
            return bool(_q.size() == _cap);
        }
        bool IsEmpty()  //是否空
        {
            return _q.empty();
        }

    public:
        BlockQueue(int cap = gcap)
        :_cap(cap),
        _cwait_num(0),
        _pwait_num(0)

        {
        }

        void Equeue(const T &in) // 生产者
        {
            LockGuard lock(_mutex); //上锁

            while(IsFull()) //判断是否满了，使用while是防止伪唤醒
            {
                //进行等待
                _pwait_num++;
                _productor_cond.Wait(_mutex);   //可能会有伪唤醒
                _pwait_num--;
            }

            //到这里肯定不为满了 - 生产数据
            _q.push(in);

            //是否有需要唤醒的消费者线程
            if(_cwait_num > 0)
            {
                _consumer_cond.NotifyAll(); //进行全部唤醒
            }
        }

        void Pop(T *out) // 消费者
        {
            LockGuard lock(_mutex); //上锁
            while(IsEmpty())
            {
                _cwait_num++;
                _consumer_cond.Wait(_mutex);
                _cwait_num--;
            }

            //到这里肯定不为空 - 消费数据
            *out = _q.front();
            _q.pop();

            //是否有需要唤醒的生产者线程
            if(_pwait_num > 0)
            {
                _productor_cond.NotifyAll();
            }

        }

        ~BlockQueue()
        {
        }

    private:
        std::queue<T> _q;     // 保存数据的容器，临界资源
        int _cap;             // bq最大容量
        Mutex _mutex;         // 互斥
        Cond _productor_cond; // 生产者条件变量
        Cond _consumer_cond;  // 消费者条件变量

        int _cwait_num;       //正在等待的消费者线程数量
        int _pwait_num;       //正在等待的生产者线程数量
    };
}